//methods to test the functions in word32

#include "test_word32.h"

//main method
//calls the other methods
//return true if all test pass
//false if one of the tests fail
bool test_word32(){
  std::cout << "Testing word32" << std::endl;
  
  //test the different constructors and 
  //how to convert into a string
  bool status = test_w32_constructAndToString();
  if(status==false)
    return status;
  std::cout << "Passed: constructor and toString\n";
  
  //test getBit
  status=test_w32_get();
  if(status==false)
    return false;
  std::cout<< "Passed: getBit()\n";
  
  //test setBit
  status=test_w32_set();
  if(status==false)
    return false;
  std::cout<< "Passed: setBit()\n";
  
  //test clearBit
  status=test_w32_clear();
  if(status==false)
    return false;
  std::cout<< "Passed: clearBit()\n";

  //test setVal
  status=test_w32_val();
  if(status==false)
    return false;
  std::cout<< "Passed: setVal()\n";

  //test logical operators
  status=test_w32_op();
  if(status==false)
    return false;
  std::cout<< "Passed: test_op()\n";

  //test logical operators
  status=test_w32_arithmetic();
  if(status==false)
    return false;
  std::cout<< "Passed: test_arithmetic()\n";
  
  //test shift operators
  status=test_w32_shift();
  if(status==false)
    return false;
  std::cout<< "Passed: test_shift()\n";
  
  //test weight
  status=test_w32_weight();
  if(status==false)
    return false;
  std::cout<< "Passed: test_weight()\n";
  
  //test equal
  status=test_w32_equal();
  if(status==false)
    return false;
  std::cout<< "Passed: test_equal()\n";
  
  //test assign
  status=test_w32_assign();
  if(status==false)
    return false;
  std::cout<< "Passed: test_assign()\n";
  
  //return true when all tests passed
  return true;
}

bool test_w32_constructAndToString(){
  word32 w1;
  //when using the default constructor the 0-word should be created
  std::string refString="00000000";
  std::string testString=w1.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failing testing default constructor\n";
    return false;
  }
  //create the following word 0xca864321
  refString="CA864321";
  word32 w2(0xca864321uL);
  //test all four different toString 
  testString=w2.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed when testing constructor from unsigned\n";
    return false;
  }
  refString="84C26153";
  testString=w2.toHexStringLSB();
  if(testString!=refString){
    std::cout << "Failed: toHexStringLSB\n";
    return false;
  }
  refString="11001010100001100100001100100001";
  testString=w2.toBinStringMSB();
  if(testString!=refString){
    std::cout << "Failed: toBinStringMSB\n";
    return false;
  }
  refString="10000100110000100110000101010011";
  testString=w2.toBinStringLSB();
  if(testString!=refString){
    std::cout << "Failed: toBinStringLSB\n";
    return false;
  }

  //test constuctor that takes another word32
  word32 w3(w2);
  //if it works then w3 will pass same test as w2 did
  w2.setBit(2);//modify w2 to check that w3 does not point to same data
  testString=w3.toBinStringLSB();
 
  if(testString!=refString){
    std::cout << "Failed: Constructor using word32\n";
    return false;
  }

  //test constructor that takes a string as input
  std::string s1="11000110";
  refString="00000063";
  word32 w4(s1);
  testString=w4.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: Constructor using string\n";
    return false;
  }
  s1+="111100111100000001011010";
  word32 w5(s1);
  refString="5A03CF63";
  testString=w5.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: Constructor using string\n";
    return false;
  }

  //return true if all test cases pass
  return true;
  
}

//test getBit
bool test_w32_get(){
  //create a 32-bit word with known pattern
  //extract bits with getBit and create testString
  //if testString equal refString then passed test
  std::string refString="10001010110011110110001111101011";
  word32 w(0x8ACF63EB);
  std::string testString="";
  for(int i=31;i>=0;i--){
    if(w.getBit(i)==0)
      testString+='0';
    else
      testString+='1';
  }
  if(testString!=refString){
    std::cout << "Failed: getBit\n";
    return false;
  }
  return true;
}

//test setBit
bool test_w32_set(){
  word32 w(0x55550000uL);
  std::string refString="55555555";
  //set all bits at even positions
  for(int i=0;i<32;i+=2)
    w.setBit(i);
  std::string testString=w.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: setBit\n";
    return false;
  }
  //set all bit to 1
  for(int i=0;i<32;i++)
    w.setBit(i);
  refString="FFFFFFFF";
  testString=w.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: setBit\n";
    return false;
  }
  return true;
}


//test clearBit
bool test_w32_clear(){
  word32 w(0xFFFF5555uL);
  std::string refString="AAAA0000";
  //clear all bits at even positions
  for(int i=0;i<32;i+=2)
    w.clearBit(i);
  std::string testString=w.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: clearBit\n";
    return false;
  }
  //clear all bits 
  for(int i=0;i<32;i++)
    w.clearBit(i);
  refString="00000000";
  testString=w.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: clearBit\n";
    return false;
  }

  return true;
}

//test setVal
bool test_w32_val(){
  std::string refString="AAAAAAAA";
  word32 w(0xffaa5500);
  //set even bits to 0 and odd bits to 1
  for(int i=0;i<32;i++)
    w.setVal(i,i&1);
  std::string testString=w.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: setVal\n";
    return false;
  }
  return true;
}

//test logical operators
bool test_w32_op(){
  word32 w1(0x5555AAAAuL);
  word32 w2(0x55AA55AAuL);
  //test and
  std::string refString="550000AA";
  word32 wAnd(w1);
  wAnd.andOp(w2);
  std::string testString=wAnd.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_op (and)\n";
    return false;
  }
  //test or
  refString="55FFFFAA";
  word32 wOr(w1);
  wOr.orOp(w2);
  testString=wOr.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_op (or)\n";
    return false;
  }
  //test xor
  refString="00FFFF00";
  word32 wXor(w1);
  wXor.xorOp(w2);
  testString=wXor.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_op (xor)\n";
    return false;
  }
  //test not
  refString="AAAA5555";
  word32 wNot(w1);
  wNot.notOp();
  testString=wNot.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_op (not)\n";
    return false;
  }
  return true;
}

//test arithmetic
bool test_w32_arithmetic(){
  word32 w1(0xCCCCCCCCuL);
  word32 w2(0x81C04321uL);
  //test add
  word32 wAdd(w1);
  wAdd.add(w2);
  std::string refString="4E8D0FED";
  std::string testString=wAdd.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_arithmetic (add)" << std::endl;
    return false;
  }
  //test sub without carry high bit
  word32 wSub(w1);
  wSub.sub(w2);
  refString="4B0C89AB";
  testString=wSub.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_arithmetic (sub)" << std::endl;
    return false;
  }
  //test sub with carry high bit
  word32 wSubc(w2);
  wSubc.sub(w1);
  refString="B4F37655";
  testString=wSubc.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_arithmetic (sub with carry)" << std::endl;
    return false;
  }
  return true;
}

//test shift operators
bool test_w32_shift(){
  word32 w(0x842137EFuL);
  //test left shift
  word32 wL(w);
  wL.lshift(4);
  std::string refString="42137EF0";
  std::string testString=wL.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_shift (left shift)" << std::endl;
    return false;
  }
  //test right shift
  word32 wR(w);
  wR.rshift(8);
  refString="00842137";
  testString=wR.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_shift (right shift)" << std::endl;
    return false;
  }
  //test circular shift
  word32 wC(w);
  wC.cshift(12);
  refString="137EF842";
  testString=wC.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: test_shift (circular shift)" << std::endl;
    return false;
  }
  return true;
}


//test weight
bool test_w32_weight(){
  //test word with all 0
  word32 w1(0);
  if(w1.weight()!=0){
    std::cout << "Failed: weight (0)\n";
    return false; 
  }
  //test word with all 1
  word32 w2(0xffffffffuL);
  if(w2.weight()!=32){
    std::cout << "Failed: weight (1)\n";
    return false; 
  }
  //test a third word
  word32 w3(0xf0123abcuL);
  if(w3.weight()!=15){
    std::cout << "Failed: weight (f0123abc)\n";
    return false; 
  }
  return true;
}

//test equal
bool test_w32_equal(){
  word32 w1(0x12345678uL);
  word32 w2(w1);
  //test words that are equal
  if(!w1.equal(w2)){
    std::cout << "Failed: equal (equality)\n";
    return false;
  } 
  //test words that are inequal
  word32 w3(0x5436ababuL);
  if(w1.equal(w3)){
    std::cout << "Failed: equal (inequality)\n";
    return false;
  } 
  return true;
}

//test assign
bool test_w32_assign(){
  word32 w1(0xca864321uL);
  word32 w2(0x12345678uL);
  w2.assign(w1);
  //test assign with another word
  std::string refString="CA864321";
  std::string testString=w2.toHexStringMSB();
  if(testString!=refString){
   std::cout << "Failed when testing assign with another word\n";
   return false;
  }
  //test assign with an unsigned integer
  refString="84C26153";
  w2.assign(0x84C26153uL);
  testString=w2.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: testing assign with unsigned long\n";
    return false;
  }
  //test assign with string
  std::string s1="11000110";
  refString="00000063";
  w2.assign(s1);
  testString=w2.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: Assign with string\n";
    return false;
  }
  s1+="111100111100000001011010";
  w2.assign(s1);
  refString="5A03CF63";
  testString=w2.toHexStringMSB();
  if(testString!=refString){
    std::cout << "Failed: Assign with string\n";
    return false;
  }

  //return true if all test cases pass
  return true;
  
}
